﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FlightSearchServer
{
    class ServersManager
    {
        private static ServersManager _instance = new ServersManager();

        private ServersCollection registeredServers = new ServersCollection();

        private static object locker = new Object();

        private ServersManager() { }

        public static ServersManager Instance
        {
            get
            {
                return _instance;
            }
        }

        public ServersManager addNewServer(SearchServer server)
        {
            lock (locker)
            {
                if (registeredServers.Contains(server))
                {
                    // Trial to register for second time, hence was failed down.
                    // Need to flush out previous version.
                    removeServer( server.Name, server.Alliance);
                    // Now we could register again with updated data.
                } 
                registeredServers.Add(server);
                foreach (SearchServer srv in server.HasReplicationOf)
                { // Update each previously registered node.
                    var tmp = (from t in registeredServers
                               where t.Name == srv.Name && t.Alliance == srv.Alliance
                               select t).ToList();
                    if (tmp.Count == 0)
                        continue;
                    tmp.ElementAt(0).ReplicatedAt.Add(srv);
                    if (tmp.ElementAt(0).HasReplicationOf.Contains(srv))
                        srv.ReplicatedAt.Add(tmp.ElementAt(0));
                }
                return this;
            }
        }

        public ServersManager removeServer(string serverName, string alliance)
        {
            lock (locker)
            {
                var tmp = (from server in registeredServers
                           where server.Name == serverName && server.Alliance == alliance
                           select server).ToList();
                if (tmp.Count == 0)
                    return this;
                registeredServers.Remove(tmp.ElementAt(0));
                foreach (SearchServer server in registeredServers)
                {
                    // Since server is no more exist we need to 
                    // remove it from all other servers lists
                    server.HasReplicationOf.Remove(tmp.ElementAt(0)); // There are no more replications of server
                    server.ReplicatedAt.Remove(tmp.ElementAt(0)); // Since removed could not keep replications of others
                }
                return this;
            }
        }

        public ServersManager updateServer( string serverName, string alliance, ServersCollection list)
        {
            lock (locker)
            {
                var server = (from t in registeredServers // find server to update
                              where t.Name == serverName && t.Alliance == alliance
                              select t).Single( );
                foreach (SearchServer srv in list)
                {
                    var tmp = (from t in registeredServers // find server to update
                               where t.Name == srv.Name && t.Alliance == srv.Alliance
                               select t).Single();
                    if (tmp == null)
                        continue;
                    if ( !tmp.HasReplicationOf.Contains( server))
                        tmp.HasReplicationOf.Add( server);
                    if ( !server.ReplicatedAt.Contains( tmp))
                        server.ReplicatedAt.Add( tmp);
                }
                return this;
            }
        }

        public ServersCollection getServersCopy()
        {
            lock (locker)
            {
                return new ServersCollection( registeredServers);
            }
        }

        public void replicateAt(SearchServer srv, SearchServer server)
        {
            lock (locker)
            {
                server.ReplicatedAt.Add(srv);
                var tmp = (from t in registeredServers
                           where t.Name == srv.Name && t.Alliance == srv.Alliance
                           select t).Single();
                tmp.HasReplicationOf.Add(server);
            }
        }

        public void markAsFailed(SearchServer server)
        {
            lock (locker)
            {
                var tmp = (from t in registeredServers
                           where t.Name == server.Name && t.Alliance == server.Alliance
                           select t).Single();
                if (tmp != null)
                    tmp.Failed = true;
            }
        }
    }
}